package sueca2011;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Random;

import java.io.*;
import java.rmi.RemoteException;
import java.util.Observer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 *
 * @author Hugo, gabrieloliveira
 */
public class Jogo extends Observable implements Observer{
    private Carta cartaTrunfo;
    private Time timeVencedor;
    private Rodada rodadaAtual;
    private ArrayList<Rodada> historicoRodadas;
    private Time timeA,timeB;
    private Jogador jogadorServidor;
    private Carta cartas[]; // Adicionar no diagrama de classes
    private int serverPort = 2000;
    private Rodada rodadas[];
    private int rodada;
    private Jogador ordemMesa[];
    private Jogador donoTrunfo;
    
    public Jogo(Time timeA,Time timeB) throws RemoteException{
        this.timeA = timeA;
        this.timeB = timeB;
        rodada=0;
        cartas = new Carta[40];
        this.historicoRodadas = new ArrayList<Rodada>();
        for(int i=0,count=0;i<4;i++)//NIPES
            for(int j=0;j<10;j++)//NUMERO
                cartas[count++] = new Carta(i, j);
        rodadas = new Rodada[10];
        for(int i=0;i<rodadas.length;i++)
            rodadas[i] = new Rodada(this);

        ordemMesa = new Jogador[4];
        ordemMesa[0] = timeA.getJogador1();
        ordemMesa[1] = timeB.getJogador2();
        ordemMesa[2] = timeA.getJogador2();
        ordemMesa[3] = timeB.getJogador1();

        for(int i=0;i<ordemMesa.length;i++)
            if(ordemMesa[i] instanceof IA)
                this.addObserver((IA)ordemMesa[i]);
        this.notifyObservers();
        this.setChanged();
        
        this.inicializar();
    }

    public void addObserverNasRodadas(Observer ob){
        if(this.rodadas!=null){
            for(int i=0;i<rodadas.length;i++)
                rodadas[i].addObserver(ob);
        }
    }

    private void escolheTrunfo(){
        int rand = new Random().nextInt(cartas.length);
        this.cartaTrunfo = cartas[rand];
    }

    private void distribuiCartas() throws RemoteException{
        Random rand = new Random();
        Carta aux;
        int randomInt;
        int i, indiceTrunfo=0;
        this.donoTrunfo = null;
        for (i=cartas.length-1; i>=0; i--){
           randomInt = (int) Math.floor(Math.random()*i);
           aux = cartas[randomInt];
           cartas[randomInt]=cartas[i];
           cartas[i]=aux;
           if (cartas[i]==this.getTrunfo())
               indiceTrunfo=i;
        }

        for (i=0; i<10;i++){
            getTimeA().getJogador1().addCartaInicial(cartas[i]);
            if (i==indiceTrunfo)
                this.donoTrunfo = getTimeA().getJogador1();
        }
        for (;i<20;i++){
            getTimeA().getJogador2().addCartaInicial(cartas[i]);
            if (i==indiceTrunfo)
                this.donoTrunfo = getTimeA().getJogador2();
        }
        for (;i<30;i++){
            getTimeB().getJogador1().addCartaInicial(cartas[i]);
           if (i==indiceTrunfo)
                this.donoTrunfo = getTimeB().getJogador1();
        }
        for (; i<cartas.length;i++){
            getTimeB().getJogador2().addCartaInicial(cartas[i]);
           if (i==indiceTrunfo)
                this.donoTrunfo = getTimeB().getJogador2();
        }



    }

    public boolean realizaJogada(Jogador jogador) throws RemoteException{
        Jogada jogada = jogador.getJogadaEscolhida();
        boolean aceita = false;
        if(validaJogada(jogada))
            if(this.getRodadaAtual().realizaJogada(jogada))
                aceita = true;
        
        if(aceita)
            jogador.jogadaFoiAceita(jogada.getCartaJogada());
        else
            jogador.jogadaNaoFoiAceita(jogada.getCartaJogada());
        return aceita;
    }

    public boolean validaJogada(Jogada jogada) throws RemoteException{
        if((this.rodadaAtual.equals(this.rodadas[0]) && jogada.getJogador()==this.getTimeA().getJogador1()) )
            return true;
        else if ((this.rodadaAtual.equals(this.rodadas[0]) && jogada.getJogador()!=this.getTimeA().getJogador1()) ||jogada.getJogador() != this.historicoRodadas.get(this.historicoRodadas.size()-1).getVencedor()){// verifica se é o vencedor da ultima rodada
            if(jogada.getCartaJogada().getNipe()==this.rodadaAtual.getPuxadaAtual().getNipe())
                return true;
            else
                return !jogada.getJogador().temCartaComPuxada(this.rodadaAtual.getPuxadaAtual().getNipe());
        }else
            return true;
    }

    private void inicializar() throws RemoteException{
        this.escolheTrunfo();
        this.distribuiCartas();
        
    }

    public void proximaRodada() throws RemoteException{
        Jogador ordem[] = new Jogador[4];
        ordem[0] = this.historicoRodadas.get(this.historicoRodadas.size()-1).getVencedor();
        int start=0;
        for(int i=0;i<this.ordemMesa.length;i++)
            if(ordemMesa[i]==ordem[0])
                start = i;
        for(int i=start+1,j=1;i<ordem.length+start;i++,j++){
            ordem[j]=this.ordemMesa[i%ordemMesa.length];
       }

        
        this.rodadaAtual = rodadas[rodada++];
        this.rodadaAtual.setOrdem(ordem);   

        rodadaAtual.adicionaObserver(this);
        Thread tRodada = new Thread(rodadaAtual);
        tRodada.start();
    }

    public void iniciar() throws RemoteException{

        
        this.rodadaAtual = rodadas[rodada++];
        this.rodadaAtual.setOrdem(this.ordemMesa);

        this.notifyObservers(new AlertaInicioJogo(cartaTrunfo, cartas));
        this.setChanged();

        rodadaAtual.adicionaObserver(this);
        Thread tRodada = new Thread(rodadaAtual);
        tRodada.start();
        
    }

    public static int valorCarta(Carta carta){
        int pt=0;

        switch(carta.getNumero()){
            case Carta.CARTA_AS:
                pt = 11;
                break;
            case Carta.CARTA_7S:
                pt = 10;
                break;
            case Carta.CARTA_REI:
                pt = 4;
                break;
            case Carta.CARTA_VALETE:
                pt = 3;
                break;
            case Carta.CARTA_DAMA:
                pt = 2;
                break;
        }

        return pt;
    }

    public Rodada getRodadaAtual() {
        return rodadaAtual;
    }

    public Carta getTrunfo(){
        return cartaTrunfo;
    }

    public void update(Observable o, Object o1) {
        Jogador jogador;
        if(o instanceof Rodada && o1 instanceof FimRodada){
            try {
                this.historicoRodadas.add(rodadaAtual);
                if(rodada<10)this.proximaRodada();
                else{
                    try{
                        if(this.getTimeA().getPontos()>this.getTimeB().getPontos())
                            System.out.println("Vencedores ("+this.getTimeA().getPontos()+" pts / "+this.getTimeB().getPontos()+" pts): "+this.getTimeA().getJogador1().getNome()+","+this.getTimeA().getJogador2().getNome());
                        else if(this.getTimeA().getPontos() < this.getTimeB().getPontos())
                            System.out.println("Vencedores ("+this.getTimeB().getPontos()+" pts / "+this.getTimeA().getPontos()+" pts): "+this.getTimeB().getJogador1().getNome()+","+this.getTimeB().getJogador2().getNome());
                        else if(this.getTimeA().getPontos() == this.getTimeB().getPontos())
                            System.out.println("Empate ("+this.getTimeA().getPontos()+" pts)!");
                    
                    }catch(RemoteException e){
                        e.printStackTrace();
                    }
                }
            } catch (RemoteException ex) {
                Logger.getLogger(Jogo.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }


    public Time getTimeA() {
        return timeA;
    }


    public Time getTimeB() {
        return timeB;
    }

    public Jogador getDonoTrunfo(){
        return donoTrunfo;
    }
}
